<!--
  Copyright 2018 Google LLC

  Licensed under the Apache License, Version 2.0 (the "License");
  you may not use this file except in compliance with the License.
  You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.
-->
<link rel="import" href="../../bower_components/polymer/polymer.html">
<link rel="import" href="../../bower_components/iron-ajax/iron-ajax.html">
<link rel="import" href="../../bower_components/paper-button/paper-button.html">
<link rel="import" href="../../bower_components/paper-spinner/paper-spinner.html">
<link rel="import" href="../../bower_components/paper-styles/color.html">
<link rel="import" href="../common/paginated-list/paginated-list.html">
<link rel="import" href="upload-form.html">

<dom-module id="upload-testcase">
  <link rel="import" href="../../stylesheets/main.css" type="css">
  <link rel="import" href="../technology/technology.css" type="css">
  <script src="../../javascripts/error.js" type="text/javascript"></script>
  <template>
    <style>
      :host {
        display: block;
      }

      :host .error {
        text-align: left;
        color: #ca4545;
        display: block;
        padding: 30px 60px;
        box-shadow: 0px 3px 6px rgba(0, 0, 0, 0.2);
        background-color: #f2f7fa;
      }

      :host .error .title {
        display: block;
        font-weight: bold;
      }

      :host .error .trace-dump {
        display: block;
        overflow: auto;
        font-family: 'Source Code Pro', monospace;
        white-space: pre;
        margin-top: 10px;
        font-size: 12px;
      }

      :host .empty {
        text-align: center;
        color: #777;
        display: block;
        padding: 30px;
        font-weight: bold;
        font-size: 18px;
        box-shadow: 0px 3px 6px rgba(0, 0, 0, 0.2);
        background-color: #f2f7fa;
      }

      :host .testcases {
        display: block;
        overflow: hidden;
        box-shadow: 0px 3px 6px rgba(0, 0, 0, 0.2);
        background-color: #f2f7fa;
      }

      :host .testcases > div {
        display: block;
        margin: 0px;
        padding: 5px 0px;
      }

      :host .testcases > div.closed {
        background-color: #e7f7f5;
      }

      :host .testcases > div.row:first-child {
        border-top: 0px solid #ccc;
      }

      :host .testcases > div.row {
        border-top: 1px solid #ccc;
      }

      :host .testcases > div.row > table {
        width: 100%;
      }

      :host .testcases > div.row > table td {
        vertical-align: top;
      }

      :host .testcases > div.row > table tr > td {
        text-align: left;
        line-height: 22px;
        padding-left: 5px;
      }

      :host .testcases > div.row > table tr > td.main {
        font-weight: bold;
        font-size: 14px;
      }

      :host .testcases > div.row > table tr > td.right {
        text-align: right;
      }

      :host .testcases > div.row a {
        color: #333;
        text-decoration: none;
        cursor: pointer;
      }

      :host .testcases > div.row a:visited {
        color: #551A8B !important;
        text-decoration: none;
      }

      :host .testcases > div.row a:hover {
        color: #4078c0 !important;
      }

      :host .testcases > div.row .states span {
        display: block;
      }

      :host .testcases > div.row .states {
        font-weight: normal;
        font-family: 'Source Code Pro', monospace;
        text-decoration: none;
        white-space: nowrap;
        overflow: hidden;
        display: inline-block;
        line-height: 22px;
        font-size: 13px;
      }

      :host .testcases > div.row span.subtitle {
        display: inline-block;
        font-size: 12px;
        color: #666;
        font-weight: normal;
        margin-left: 5px;
      }

      :host .testcases > div.row .unimportant {
        display: block;
        font-size: 12px;
        color: #666;
        font-weight: normal;
        margin-top: 5px;
      }

      :host .testcases > div.row .label {
        font-size: 13px;
        display: inline-block;
        margin-left: 5px;
        border: 1px solid #888;
        color: #888;
        padding: 2px 4px;
        vertical-align: middle;
        font-weight: normal;
        line-height: 12px;
        text-decoration: none;
      }

      :host .testcases > div.row span.confirmed {
        border: 1px solid #1D8301;
        color: #1D8301;
      }

      :host .testcases > div.row span.duplicated {
        border: 1px solid #5319E7;
        color: #5319E7;
      }

      :host .testcases > div.row span.unreproducible {
        border: 1px solid #FC2929;
        color: #FC2929;
      }

      :host .testcases > div.row .fuzzer {
        border: 1px solid #5D4037;
        color: #5D4037;
      }

      :host .testcases > div.row .job {
        border: 1px solid #5D4037;
        color: #5D4037;
      }

      :host .testcases > div.row .issue {
        border: 1px solid #01579B;
        color: #01579B;
      }

      .toolbar {
        margin: 10px auto;
        width: 100%;
        display: -ms-flex;
        display: -webkit-flex;
        display: flex;
      }

      .toolbar .permission {
        color: #888;
        width: 50%;
        font-size: 12px;
        font-style: italic;
        line-height: 40px;
      }

      .toolbar .buttons {
        text-align: right;
        width: 50%;
      }

      #mainContent {
        min-width: 1000px;
        max-width: 1000px;
        width: 1000px;
      }

      :host .submit-button {
        position: absolute;
        top: 13px;
        right: 10px;
      }
    </style>
    <iron-ajax
      id="ajax"
      url="/upload-testcase/load"
      method="POST"
      content-type="application/json"
      body="[[ajaxParams]]"
      loading="{{loading}}"
      last-request="{{request}}"
      last-error="{{error}}"
      last-response="{{response}}"
      on-error="handleError"
      on-request="handleRequest"
      on-response="handleResponse"
      debounce-duration="500"></iron-ajax>
    <upload-form id="uploadForm" field-values="{{fieldValues}}" opened="{{uploadFormOpened}}">
    </upload-form>
    <app-toolbar>
      <paper-icon-button icon="menu" onclick="document.getElementById('drawer').toggle()"></paper-icon-button>
      <div id="title">Uploaded Testcases</div>
      <if-else condition="[[loading]]">
        <paper-spinner slot="t" active="[[loading]]"></paper-spinner>
        <paper-icon-button slot="f" hidden="[[loading]]" class="submit-button" icon="refresh" on-tap="searchButtonTapped"></paper-icon-button>
      </if-else>
    </app-toolbar>
    <div id="mainContent">
      <div class="toolbar">
        <div class="permission">[[getPermissionText(params)]]</div>
        <div class="buttons"><paper-button raised toggles active="{{uploadFormOpened}}">Upload</paper-button></div>
      </div>
      <paginated-list
          id="paginatedList"
          result="{{result}}"
          params="{{params}}"
          keys="[[paramKeys]]"
          loading="[[loading]]"
          on-params-modified="paramsModified">
        <div class="error" hidden$="[[!shouldShowError(result, result.error)]]">
          <span class="title">[[result.error.message]]</span>
          <span class="trace-dump" hidden$="[[!result.error.traceDump]]">[[result.error.traceDump]]</span>
        </div>
        <div class="empty" hidden$="[[!shouldShowEmpty(result, result.items)]]">
          No uploaded testcases found
        </div>
        <div class="testcases" hidden$="[[!shouldShowItems(result, result.items)]]">
          <template is="dom-repeat" items="[[result.items]]">
            <div class="row">
              <table>
                <tr>
                  <td class="main">
                    <a href$="/testcase-detail/[[item.testcaseId]]">
                      [[getCrashType(item)]]
                      <span class="subtitle" title="Creation time">[[formatTime(item.timestamp)]]</span>
                      <span class="subtitle" title="Uploader">[[item.uploaderEmail]]</span>
                    </a>
                  </td>
                  <td class="right">
                    <template is="dom-if" if="[[item.testcase.issueNumber]]">
                      <a class="label issue" href="/issue/[[item.testcaseId]]/[[item.testcase.issueNumber]]" title="Click to view the associated issue [[item.testcase.issueNumber]] in the issue tracker.">Issue [[item.testcase.issueNumber]]</a>
                    </template>
                    <template is="dom-repeat" items="[[getStatus(item)]]" as="status">
                      <span class$="label [[status.className]]" title$="[[status.tooltip]]">[[status.label]]</span>
                    </template>
                  </td>
                </tr>
                <tr>
                  <td>
                    <a href="/testcase-detail/[[item.testcaseId]]">
                      <span class="states">
                        <template is="dom-repeat" items="[[item.testcase.crashStateLines]]" as="state">
                          <span>[[state]]</span>
                        </template>
                      </span>
                    </a>
                  </td>
                  <td class="right">
                    <div>
                      <template is="dom-if" if="[[item.testcase.fuzzerName]]">
                        <span class="label fuzzer">Fuzzer [[item.testcase.fuzzerName]]</span>
                      </template>
                      <template is="dom-if" if="[[item.testcase.job]]">
                        <span class="label job">Job [[item.testcase.job]]</span>
                      </template>
                    </div>
                    <span class="unimportant" title="Filename">[[computeFilename(item)]]</span>
                  </td>
                </tr>
              </table>
            </div>
          </template>
        </div>
      </paginated-list>
    </div>
  </template>
  <script>
    class UploadTestcase extends Polymer.Element {
      static get is() { return 'upload-testcase'; }

      static get properties() {
        return {
          result:  Object,
          fieldValues:  Object,
          params: Object,
          paramKeys: {
            type: Array,
            value: ['page']
          },
          response: Object,
          loading: {
            type: Boolean,
            value: false
          },
          uploadFormOpened: {
            type: Boolean,
            value: () => {
              return !!window.query.getParam('upload');
            }
          }
        };
      }

      shouldShowError(result, error) {
        return !!error;
      }

      shouldShowEmpty(result, items) {
        return items && items.length == 0;
      }

      shouldShowItems(result, items) {
        return items && items.length > 0;
      }

      ready() {
        super.ready();

        if (this.result) {
          this.set('result.error', this.result.error || null);
        }

        this.paramKeys.forEach((key) => {
          if (!this.params[key]) {
            this.set(`params.${key}`, '');
          }
        });

        this.$.paginatedList.init();
      }

      searchButtonTapped(ev) {
        this.params.page = '';
        this.set('result.page', 1);
        this.search();
      }

      paramsModified(ev) {
        this.search();
      }

      search() {
        this.ajaxParams = {};
        let hasParam = false;
        this.paramKeys.forEach((key) => {
          if (this.params[key]) {
            hasParam = key != 'page' && true;
            this.ajaxParams[key] = this.params[key];
          }
        });

        this.$.paginatedList.updateQuery();
        this.$.ajax.generateRequest();
      }

      handleRequest() {
        var reqs = this.$.ajax.activeRequests;
        for (let i=0;i<reqs.length;i++) {
          if (reqs[i] != this.request) {
            reqs[i].abort();
          }
        }
      }

      handleResponse() {
        this.result = this.response;
        this.set('result.error', null);
        this.$.paginatedList.save();
      }

      handleError() {
        let error = parseError(this.error);
        if (!error) {
          // this.error is undefined because its request is aborted.
          return;
        }

        this.set('result.items', null);
        this.set('result.error', error);

        this.$.paginatedList.save();
      }

      formatTime(secondsFromEpoch) {
        let date = new Date(secondsFromEpoch * 1000);
        return date.toLocaleDateString(
            'en-US',
            {weekday:'short', year:'numeric', month:'short', day:'numeric',
             hour:'numeric', minute:'numeric'});
      }

      getPermissionText(params) {
        if (params.permission) {
          return `You're only allowed to see the testcases uploaded by ${params.permission.uploaderEmail}.`;
        } else {
          return "You're allowed to see everything.";
        }
      }

      getCrashType(item) {
        if (item.status == 'Pending') {
          return 'Pending';
        } else if (item.status == 'Unreproducible') {
          return 'NA';
        } else if (item.testcase && item.testcase.crashType) {
          return item.testcase.crashType;
        } else {
          return 'NA';
        }
      }

      getStatus(item) {
        if (item.status == 'Confirmed') {
          return [{
               label: 'Confirmed',
               tooltip: 'The testcase causes a crash.',
               className: 'confirmed'
          }];
        } else if (item.status == 'Unreproducible') {
          return [{
               label: 'Unreproducible',
               tooltip: 'The testcase does NOT cause a crash.',
               className: 'unreproducible'
          }];
        } else if (item.status == 'Duplicate') {
          return [{
               label: 'Duplicated',
               tooltip: 'The testcase causes a known crash.',
               className: 'duplicated'
          }];
        } else if (item.status == 'Irrelevant') {
          return [{
               label: 'Ignored',
               tooltip: "The testcase's crash signature is explicitly ignored.",
               className: 'ignored'
          }];
        } else {
          return [{
               label: item.status,
               tooltip: item.status,
               className: ''
          }];
        }
      }

      computeFilename(item) {
        if (item.bundled) {
          if (item.pathInArchive) {
            return `${item.pathInArchive} in ${item.filename}`;
          } else {
            return `Multiple testcases in ${item.filename}`;
          }
        } else {
          return item.filename;
        }
      }
    }

    customElements.define(UploadTestcase.is, UploadTestcase);
  </script>
</dom-module>


